<template>
  <span class="animated-number" :style="{ color: textColor }">
    {{ displayValue }}
  </span>
</template>

<script setup lang="ts">
import { ref, watch, onMounted } from 'vue'

interface Props {
  value: number
  duration?: number
  decimals?: number
  prefix?: string
  suffix?: string
  textColor?: string
  easing?: 'linear' | 'easeInOut' | 'easeOut' | 'easeIn' | 'easeInOutCubic' | 'easeOutQuart' | 'easeInOutQuart'
}

const props = withDefaults(defineProps<Props>(), {
  duration: 1000,
  decimals: 2,
  prefix: '',
  suffix: '',
  textColor: 'inherit',
  easing: 'easeOutQuart'
})

const displayValue = ref('')
const currentValue = ref(0)
let animationId: number | null = null

// 高质量缓动函数 - 更流畅的动画曲线
const easingFunctions = {
  linear: (t: number) => t,
  easeInOut: (t: number) => t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
  easeOut: (t: number) => t * (2 - t),
  easeIn: (t: number) => t * t,
  // 三次贝塞尔曲线 - 非常平滑
  easeInOutCubic: (t: number) => t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1,
  // 四次方缓出 - 快速开始，平滑结束
  easeOutQuart: (t: number) => 1 - (--t) * t * t * t,
  // 四次方缓入缓出 - 最平滑的效果
  easeInOutQuart: (t: number) => t < 0.5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t
}

// 格式化数字
const formatNumber = (num: number): string => {
  const formatted = num.toFixed(props.decimals)
  return `${props.prefix}${formatted}${props.suffix}`
}

// 高精度数字动画函数 - 超流畅效果
const animateNumber = (from: number, to: number) => {
  if (animationId) {
    cancelAnimationFrame(animationId)
  }

  const startTime = performance.now()
  const difference = to - from

  // 如果差值很小，直接设置最终值
  if (Math.abs(difference) < 0.01) {
    currentValue.value = to
    displayValue.value = formatNumber(to)
    return
  }

  const animate = (currentTime: number) => {
    const elapsed = currentTime - startTime
    let progress = Math.min(elapsed / props.duration, 1)

    // 应用缓动函数
    const easedProgress = easingFunctions[props.easing](progress)

    // 高精度计算当前值
    const current = from + (difference * easedProgress)

    // 平滑更新值
    currentValue.value = current

    // 优化数字格式化 - 避免频繁的字符串操作
    if (progress >= 1) {
      // 最终值，确保精确
      currentValue.value = to
      displayValue.value = formatNumber(to)
      animationId = null
    } else {
      // 动画中，使用当前值
      displayValue.value = formatNumber(current)
      animationId = requestAnimationFrame(animate)
    }
  }

  animationId = requestAnimationFrame(animate)
}

// 监听value变化
watch(() => props.value, (newValue, oldValue) => {
  if (oldValue !== undefined && newValue !== oldValue) {
    animateNumber(currentValue.value, newValue)
  } else {
    // 初始值或相同值，直接设置
    currentValue.value = newValue
    displayValue.value = formatNumber(newValue)
  }
}, { immediate: true })

// 组件挂载时设置初始值
onMounted(() => {
  currentValue.value = props.value
  displayValue.value = formatNumber(props.value)
})

// 组件卸载时清理动画
import { onUnmounted } from 'vue'
onUnmounted(() => {
  if (animationId) {
    cancelAnimationFrame(animationId)
  }
})
</script>

<style scoped>
.animated-number {
  display: inline-block;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
  transition: color 0.3s ease;
  /* 确保数字宽度固定，避免布局跳动 */
  font-family: 'SF Mono', 'Monaco', 'Inconsolata', 'Roboto Mono', 'Source Code Pro', monospace;
  /* 硬件加速，提升动画性能 */
  transform: translateZ(0);
  will-change: contents;
  /* 防止文本选择影响动画 */
  user-select: none;
  -webkit-user-select: none;
  /* 优化渲染 */
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}
</style>
